package work.chenbo.springboot.security.authentication;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import work.chenbo.springboot.security.config.properties.SecurityProperties;
import work.chenbo.springboot.security.exception.ValidateCodeException;
import work.chenbo.starter.common.util.ImageCodeUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Component
@Slf4j
@RequiredArgsConstructor
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {


    private final SecurityProperties securityProperties;

    private final AuthenticationFailureHandler authenticationFailureHandler;

    private List<String> urls;

    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();
        SecurityProperties.ImageCode imageProperties = securityProperties.getImageCode();
        List<String> urls = imageProperties.getUrls();
        if(urls==null){
            urls = new ArrayList<>();
        }
        urls.add("/login/form");
        this.urls = urls.stream().filter(StringUtils::isNoneBlank).distinct().collect(Collectors.toList());
    }

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        boolean action = false;
        AntPathMatcher pathMatcher = new AntPathMatcher();
        action = this.urls.stream()
                .anyMatch(url -> pathMatcher.match(url, httpServletRequest.getRequestURI()));
        if(action){
            log.info("请求:{}需求要验证图形验证码",httpServletRequest.getRequestURI());
            try{
                this.validate(httpServletRequest);
                filterChain.doFilter(httpServletRequest,httpServletResponse);
            }catch (ValidateCodeException validateCodeException){
                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,validateCodeException);
            }
        }else {
            filterChain.doFilter(httpServletRequest,httpServletResponse);
        }

    }

    private void validate(HttpServletRequest request) throws ValidateCodeException {
        HttpSession session = request.getSession();
        ImageCodeUtils.ImageCode imageCode = (ImageCodeUtils.ImageCode)session.getAttribute("SESSION_KEY_IMAGE_CODE");
        String codeInRequest = ServletRequestUtils.getStringParameter(request,"code","");
        if(StringUtils.isBlank(codeInRequest)){
            log.error("请求{}图形验证码为空",request.getRequestURI());
            throw new ValidateCodeException("图形验证码不能为空");
        }
        else if(imageCode == null || imageCode.isExpired()){
            log.error("请求{}图形验证码已过期",request.getRequestURI());
            throw new ValidateCodeException("图形验证码已过期请重新获取");
        }
        else if(!StringUtils.endsWithIgnoreCase(imageCode.getCode(),codeInRequest)){
            log.error("请求{}图形验证码{}与存储的验证码{}不匹配",request.getRequestURI(),codeInRequest,imageCode.getCode());
            throw new ValidateCodeException("图形验证码不正确");
        }else {
            session.removeAttribute("SESSION_KEY_IMAGE_CODE");
        }

    }
}
